#include "pp_generator.h"
#include "cpp_generator.h"
#include "csharp_generator.h"
#include "c_generator.h"
#include "go_generator.h"

namespace pp
{
    std::string GetMapValue(const std::map<std::string, std::string>& attrs, const std::string& name)
    {
        auto iter = attrs.find(name);
        if (iter != attrs.end()) return iter->second;
        return "";
    }

    void CodeHandler::StartElement(const std::string& name, const std::map<std::string, std::string>& attrs)
    {
        if (name == "struct" || name == "message")
        {
            m_oCurStruct = std::make_shared<StructInfo>(GetMapValue(attrs, "name"), STRUCT_TYPE_MESSAGE,
                GetMapValue(attrs, "desc"));
        }
        else if (name == "union" || name == "oneof")
        {
            m_oCurStruct = std::make_shared<StructInfo>(GetMapValue(attrs, "name"), STRUCT_TYPE_ONEOF,
                GetMapValue(attrs, "desc"));
        }
        else if (name == "enum")
        {
            m_oCurStruct = std::make_shared<StructInfo>(GetMapValue(attrs, "name"), STRUCT_TYPE_ENUM,
                GetMapValue(attrs, "desc"));
        }
        else if (name == "field" && m_oCurStruct)
        {
            auto fieldInfo = std::make_shared<FieldInfo>(GetMapValue(attrs, "name"), GetMapValue(attrs, "tag"),
                GetMapValue(attrs, "type"), GetMapValue(attrs, "subtype"), GetMapValue(attrs, "value"),
                GetMapValue(attrs, "default"), GetMapValue(attrs, "desc"), GetMapValue(attrs, "size"),
                GetMapValue(attrs, "json_name"), GetMapValue(attrs, "json_omitempty"));
            m_oCurStruct->AddField(fieldInfo);
        }
        else if (name == "macro")
        {
            auto fieldInfo = std::make_shared<FieldInfo>(GetMapValue(attrs, "name"), "", GetMapValue(attrs, "type"), "",
                GetMapValue(attrs, "value"), "", GetMapValue(attrs, "desc"));

            if (m_oCurStruct)
            {
                m_oCurStruct->AddField(fieldInfo);
            }
            else
            {
                m_oCodeGenerator->HandleConst(fieldInfo);
            }
        }
        else if (name == "option")
        {
            m_oCodeGenerator->HandleOption(GetMapValue(attrs, "name"), GetMapValue(attrs, "value"));
        }
        else if (name == "package")
        {
            m_oCodeGenerator->HandlePackage(GetMapValue(attrs, "name"));
        }
        else if (name == "import")
        {
            m_oCodeGenerator->HandleImport(GetMapValue(attrs, "value"));
        }
    }

    void CodeHandler::EndElement(const std::string& name)
    {
        if (name == "struct" || name == "message" || name == "union" || name == "oneof")
        {
            m_oCodeGenerator->HandleStruct(m_oCurStruct);
            m_oCurStruct.reset();
        }
        else if (name == "enum")
        {
            m_oCodeGenerator->HandleEnum(m_oCurStruct);
            m_oCurStruct.reset();
        }
    }

    Generator::Generator(const std::string& language, const std::string& srcdir, const std::string& outdir)
    {
        if (language == "cpp")
        {
            m_oCodeGenerator = std::make_shared<CppGenerator>(srcdir, outdir);
            m_oCodeHandler = std::make_shared<CodeHandler>(m_oCodeGenerator);
            m_oParser.SetHandler(m_oCodeHandler);
        }
        else if (language == "cs" || language == "csharp")
        {
            m_oCodeGenerator = std::make_shared<CSharpGenerator>(srcdir, outdir);
            m_oCodeHandler = std::make_shared<CodeHandler>(m_oCodeGenerator);
            m_oParser.SetHandler(m_oCodeHandler);
        }
        else if (language == "c")
        {
            m_oCodeGenerator = std::make_shared<CGenerator>(srcdir, outdir);
            m_oCodeHandler = std::make_shared<CodeHandler>(m_oCodeGenerator);
            m_oParser.SetHandler(m_oCodeHandler);
        }
        else if (language == "go")
        {
            m_oCodeGenerator = std::make_shared<GoGenerator>(srcdir, outdir);
            m_oCodeHandler = std::make_shared<CodeHandler>(m_oCodeGenerator);
            m_oParser.SetHandler(m_oCodeHandler);
        }
        else
        {
            throw std::runtime_error("the language '" + language + "' is not supported");
        }
    }

    void Generator::Generate(const std::string& fname)
    {
        m_oCodeGenerator->Reset(fname);
        m_oParser.ParseFile(fname);
        m_oCodeGenerator->Generate();
    }
};
